﻿using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.ActionConstraints;
using Microsoft.AspNetCore.Mvc.ApplicationModels;
using Microsoft.AspNetCore.Mvc.ModelBinding;
using Microsoft.AspNetCore.Mvc.Routing;

namespace Luka;

/// <summary>
/// 路由约束
/// </summary>
[NotScanned]
internal class RouteConvention : IApplicationModelConvention
{
    /// <summary>
    /// 定义一个变量，保存路由前缀
    /// </summary>
    private readonly string _centralPrefix;

    /// <summary>
    /// 调用时传入指定的路由前缀
    /// </summary>
    public RouteConvention(string prefix)
    {
        _centralPrefix = prefix;
    }

    //实现Apply方法
    public void Apply(ApplicationModel application)
    {
        //遍历所有Controller
        foreach (var controller in application.Controllers)
        {
            if (!string.IsNullOrWhiteSpace(_centralPrefix))
            {
                var centralPrefix = new AttributeRouteModel(new RouteAttribute(_centralPrefix));
                // 1. 查找已经标记了RouteAttribute的控制器
                var matchedSelectors = controller.Selectors.Where(x => x.AttributeRouteModel != null).ToList();
                if (matchedSelectors.Any())
                {
                    foreach (var selectorModel in matchedSelectors)
                    {
                        // 在已有的路由上再添加一个路由前缀
                        selectorModel.AttributeRouteModel = AttributeRouteModel.CombineAttributeRouteModel(centralPrefix,
                            selectorModel.AttributeRouteModel);
                    }
                }
                //2. 没有标记RouteAttribute的控制器
                var unmatchedSelectors = controller.Selectors.Where(x => x.AttributeRouteModel == null).ToList();
                if (unmatchedSelectors.Any())
                {
                    foreach (var selectorModel in unmatchedSelectors)
                    {
                        //添加一个路由前缀
                        selectorModel.AttributeRouteModel = centralPrefix;
                    }
                }
            }
            // 方法路由约束
            foreach (var action in controller.Actions)
            {
                CreateActionRoute(action);
                CreateQueryParameters(action);
            }
        }
    }

    /// <summary>
    /// 生成方法路由
    /// </summary>
    private void CreateActionRoute(ActionModel action)
    {
        var httpMethod = action.Attributes.OfType<HttpMethodAttribute>().FirstOrDefault();
        string actionName = action.ActionMethod.Name;
        //判断是否包含 ActionNameAttribute
        var actionNameAttr = action.ActionMethod.GetAttribute<ActionNameAttribute>();
        if (actionNameAttr != null && !string.IsNullOrWhiteSpace(actionNameAttr?.Name))
        {
            actionName = actionNameAttr.Name;
        }
        //判断是否定义了控制器方法路由
        var hasAction = action.Controller.Selectors[0].AttributeRouteModel?.Template?.Contains("[action]") == false;
        // 跳过已配置请求谓词特性的配置
        if (httpMethod != null)
        {
            // 定义方法路由
            if (string.IsNullOrWhiteSpace(httpMethod?.Template) && !action.Attributes.OfType<RouteAttribute>().Any() && hasAction)
            {
                action.Selectors[0].AttributeRouteModel = new AttributeRouteModel(new RouteAttribute(actionName));
            }
        }
        else
        {
            HttpMethodAttribute httpMethodAttribute;
            string method = "POST";//默认请求类型
            string verb = actionName.ToUpper();
            if (verb.StartsWith("GET") || verb.StartsWith("QUERY"))
            {
                httpMethodAttribute = new HttpGetAttribute();
                method = "GET";
            }
            else if (verb.StartsWith("POST") || verb.StartsWith("SAVE") || verb.StartsWith("ADD"))
            {
                httpMethodAttribute = new HttpPostAttribute();
                method = "POST";
            }
            else if (verb.StartsWith("PUT") || verb.StartsWith("UPDATE") || verb.StartsWith("EDIT"))
            {
                httpMethodAttribute = new HttpPostAttribute();
                method = "PUT";
            }
            else if (verb.StartsWith("DEL") || verb.StartsWith("REMOVE"))
            {
                httpMethodAttribute = new HttpDeleteAttribute();
                method = "DELETE";
            }
            else if (verb.StartsWith("PATCH"))
            {
                httpMethodAttribute = new HttpPatchAttribute();
                method = "PATCH";
            }
            else if (verb.StartsWith("HEAD"))
            {
                httpMethodAttribute = new HttpHeadAttribute();
                method = "HEAD";
            }
            else
            {
                httpMethodAttribute = new HttpPostAttribute();
            }
            // 手动添加方法请求类型特性
            action.Selectors[0].EndpointMetadata.Insert(0, httpMethodAttribute);
            // 定义方法Request类型
            var httpMethodConstraint = new HttpMethodActionConstraint(new string[] { method });
            action.Selectors[0].ActionConstraints.Add(httpMethodConstraint);
            // 定义方法路由
            if (!action.Attributes.OfType<RouteAttribute>().Any() && hasAction)
            {
                action.Selectors[0].AttributeRouteModel = new AttributeRouteModel(new RouteAttribute(actionName));
            }
        }
    }

    /// <summary>
    /// 生成参数路由
    /// </summary>
    private void CreateQueryParameters(ActionModel action)
    {
        // 无参数
        if (action.Parameters.Count == 0)
        {
            return;
        }
        // 无 QueryParametersAttribute 特性
        if (!action.Attributes.OfType<QueryParametersAttribute>().Any())
        {
            return;
        }
        foreach (var parameter in action.Parameters)
        {
            //判断是否含有 [FromQuery] [FromBody] ... 等
            if (parameter.Attributes.Any(u => typeof(IBindingSourceMetadata).IsAssignableFrom(u.GetType())))
            {
                continue;
            }
            // 如果是文件类型，则跳过
            if (typeof(IFormFile).IsAssignableFrom(parameter.ParameterType) || typeof(IFormFileCollection).IsAssignableFrom(parameter.ParameterType))
            {
                continue;
            }
            //是否自定义路由
            var route = action.Attributes.OfType<RouteAttribute>().FirstOrDefault();
            if (route == null || string.IsNullOrWhiteSpace(route?.Template))
            {
                parameter.BindingInfo = new BindingInfo { BindingSource = BindingSource.Query };
            }
        }
    }
}